package com.xiyuan.smartutils;

import com.xiyuan.smartutils.comparator.CompareUtil;
import com.xiyuan.smartutils.constants.SignConstants;
import com.xiyuan.smartutils.lang.PrimitiveArrayUtil;
import com.xiyuan.smartutils.reflect.Unsafe;
import com.xiyuan.smartutils.sort.ComparableTimSort;
import com.xiyuan.smartutils.sort.DualPivotQuicksort;
import com.xiyuan.smartutils.sort.TimSort;
import com.xiyuan.smartutils.streams.IntStreams;

import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.*;

/**
 * 数组相关工具类
 *
 * @version v1.0.0 @author lgz 2017-8-27 新建与整理
 */
@SuppressWarnings({"unchecked", "DanglingJavadoc"})
public final class Arrays extends PrimitiveArrayUtil implements SignConstants {
    static {
        Unsafe.init();
    }
    
    private static int max0(final int other) {
        return Math.max(0, other);
    }
    
    /**
     * 获取数组对象中指定index的值，支持负数，例如-1表示倒数第一个值<br>
     * 如果数组下标越界，返回null
     *
     * @param <T>   数组元素类型
     * @param array 数组对象
     * @param index 下标，支持负数
     * @return 值
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(Object array, int index) {
        if (null == array) {
            return null;
        }
        
        if (index < 0) {
            index += Array.getLength(array);
        }
        try {
            return (T) Array.get(array, index);
        }
        catch (ArrayIndexOutOfBoundsException e) {
            return null;
        }
    }
    
    /**
     * a list view of the specified array
     *
     * @param arr the array
     * @param <T> the type of the
     * @return a list view of the specified array
     */
    @SafeVarargs
    public static <T> List<T> asList(T... arr) {
        return new ArrayList<T>(java.util.Arrays.asList(arr));
    }
    
    /**
     * 返回一个新的数组，包含从 start到尾部或首部(start<0时)的中的元素。
     *
     * @param src   原数组
     * @param start 起始位置，负位表示从尾部算起,-1表示倒数第一个(包含)
     * @return 新的数组
     */
    public static <T> T[] slice(T[] src, int start) {
        Asserts.nonNull(src, "原数组");
        int len = start < 0 ? src.length + start + 1 : src.length - start;
        T[] dest = (T[]) Array.newInstance(src.getClass().getComponentType(), len);
        
        int index = 0;
        if (start < 0) {
            for (int i = src.length + start; i >= 0; i--) {dest[index++] = src[i];}
        }
        else {
            for (int i = start; i < src.length; i++) {dest[index++] = src[i];}
        }
        
        return dest;
    }
    
    /**
     * 返回一个新的数组，包含从 start到 end(不包括该元素)的中的元素。设数组length=5：
     * 1.双正数：start=1,end=4，表示读取个数len=4-1=3，数据为数组的下标第1,2,3个。
     * 2.双负数：start=-1,end=-3，表示读取个数len=start-end=-1-(-3)=2个，数据为数组下标第4,3
     * 4.单负数，start=-1,end=2，先把end置为负数，end=2-5=-3，表示读取个数len=start-end=-1+3=2，数据为数组下标第4,3
     * 5.单正数，start=1,end=-2，先把end置为正数，end=-2+5=3，表示读取个数len=3-1=2，数据为数组下标第1,2
     *
     * @param src   原数组
     * @param start 起始位置，负位表示从尾部算起,-1表示倒数第一个开始
     * @param end   结束位置，负数表示从尾部算起,-2表示倒数第二个结束
     * @return 新的数组
     */
    public static <T> T[] slice(T[] src, int start, int end) {
        Asserts.nonNull(src);
        
        if (start < 0 && end >= 0) {// 如果起始小于0表示从后面读取，则当end>0时要计算出<0的位置
            end = end - src.length;
        }
        
        if (start >= 0 && end < 0) {// 如果从起始大于等于0表示从前面读取，当end<0时要计算出前面相对位置
            end = end + src.length;
        }
        
        int len = start < 0 ? start - end : end - start;
        T[] dest = (T[]) Array.newInstance(src.getClass().getComponentType(), len);
        int index = 0;
        if (start < 0) {
            for (int i = src.length - start; i > end; i--) {dest[index++] = src[i];}
        }
        else {
            for (int i = start; i < end; i++) {dest[index++] = src[i];}
        }
        
        return dest;
    }
    
    
    /**
     * Checks that {@code fromIndex} and {@code toIndex} are in
     * the range and throws an exception if they aren't.
     */
    private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
        if (fromIndex > toIndex) {
            throw new IllegalArgumentException(
                    "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
        }
        if (fromIndex < 0) {
            throw new ArrayIndexOutOfBoundsException(fromIndex);
        }
        if (toIndex > arrayLength) {
            throw new ArrayIndexOutOfBoundsException(toIndex);
        }
    }
    
    public static <T> Set<T> newHashSet(T... values) {
        return newHashSet(false, values);
    }
    
    public static <T> Set<T> newHashSet(boolean sort, T... values) {
        int initialCapacity = Math.max((int) (values.length / .75f) + 1, 16);
        HashSet<T> set = sort ? new LinkedHashSet<>(initialCapacity) : new HashSet<T>(initialCapacity);
        Collections.addAll(set, values);
        return set;
    }
    /*****************************************************/
    // equals
    
    /*****************************************************/
    
    
    public static boolean equals(Object[] a, Object[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(byte[] a, byte[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(char[] a, char[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(short[] a, short[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(int[] a, int[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(long[] a, long[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(float[] a, float[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean equals(double[] a, double[] a2) {
        return java.util.Arrays.equals(a, a2);
    }
    
    public static boolean isArray(Object obj) {
        return obj != null && obj.getClass().isArray();
    }
    
    /**
     * 是否包含{@code null}元素
     *
     * @param <T>   数组元素类型
     * @param array 被检查的数组
     * @return 是否包含{@code null}元素
     */
    @SuppressWarnings("unchecked")
    public static <T> boolean hasNull(T... array) {
        if (!isEmpty(array)) {
            for (T element : array) {
                if (element == null) {
                    return true;
                }
            }
        }
        return array == null;
    }
    /*****************************************************/
    // filling
    /*****************************************************/
    
    /**
     * Assigns the specified long value to each element of the specified array
     * of longs.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(long[] a, long val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified long value to each element of the specified
     * range of the specified array of longs.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(long[] a, int fromIndex, int toIndex, long val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified int value to each element of the specified array
     * of ints.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(int[] a, int val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified int value to each element of the specified
     * range of the specified array of ints.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(int[] a, int fromIndex, int toIndex, int val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified short value to each element of the specified array
     * of shorts.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(short[] a, short val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified short value to each element of the specified
     * range of the specified array of shorts.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(short[] a, int fromIndex, int toIndex, short val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified char value to each element of the specified array
     * of chars.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(char[] a, char val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified char value to each element of the specified
     * range of the specified array of chars.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(char[] a, int fromIndex, int toIndex, char val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified byte value to each element of the specified array
     * of bytes.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(byte[] a, byte val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified byte value to each element of the specified
     * range of the specified array of bytes.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(byte[] a, int fromIndex, int toIndex, byte val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified boolean value to each element of the specified
     * array of booleans.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(boolean[] a, boolean val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified boolean value to each element of the specified
     * range of the specified array of booleans.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(boolean[] a, int fromIndex, int toIndex,
                            boolean val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified double value to each element of the specified
     * array of doubles.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(double[] a, double val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified double value to each element of the specified
     * range of the specified array of doubles.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(double[] a, int fromIndex, int toIndex, double val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified float value to each element of the specified array
     * of floats.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(float[] a, float val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified float value to each element of the specified
     * range of the specified array of floats.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     */
    public static void fill(float[] a, int fromIndex, int toIndex, float val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified Object reference to each element of the specified
     * array of Objects.
     *
     * @param a   the array to be filled
     * @param val the value to be stored in all elements of the array
     * @throws ArrayStoreException if the specified value is not of a
     *                             runtime type that can be stored in the specified array
     */
    public static void fill(Object[] a, Object val) {
        for (int i = 0, len = a.length; i < len; i++) {a[i] = val;}
    }
    
    /**
     * Assigns the specified Object reference to each element of the specified
     * range of the specified array of Objects.  The range to be filled
     * extends from index <tt>fromIndex</tt>, inclusive, to index
     * <tt>toIndex</tt>, exclusive.  (If <tt>fromIndex==toIndex</tt>, the
     * range to be filled is empty.)
     *
     * @param a         the array to be filled
     * @param fromIndex the index of the first element (inclusive) to be
     *                  filled with the specified value
     * @param toIndex   the index of the last element (exclusive) to be
     *                  filled with the specified value
     * @param val       the value to be stored in all elements of the array
     * @throws IllegalArgumentException       if <tt>fromIndex &gt; toIndex</tt>
     * @throws ArrayIndexOutOfBoundsException if <tt>fromIndex &lt; 0</tt> or
     *                                        <tt>toIndex &gt; a.length</tt>
     * @throws ArrayStoreException            if the specified value is not of a
     *                                        runtime type that can be stored in the specified array
     */
    public static void fill(Object[] a, int fromIndex, int toIndex, Object val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++) {a[i] = val;}
        
    }
    
    /*****************************************************/
    // 拷贝
    /*****************************************************/
    
    
    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of exactly the same class as the original array.
     *
     * @param <T>       the class of the objects in the array
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    
    /**
     * Copies the specified array, truncating or padding with nulls (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>null</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     * The resulting array is of the class <tt>newType</tt>.
     *
     * @param <U>       the class of the objects in the original array
     * @param <T>       the class of the objects in the returned array
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @param newType   the class of the copy to be returned
     * @return a copy of the original array, truncated or padded with nulls
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     * @throws ArrayStoreException        if an element copied from
     *                                    <tt>original</tt> is not of a runtime type that can be stored in
     *                                    an array of class <tt>newType</tt>
     */
    public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object) newType == (Object) Object[].class)
                   ? (T[]) new Object[newLength]
                   : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>(byte)0</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static byte[] copyOf(byte[] original, int newLength) {
        byte[] copy = new byte[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>(short)0</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static short[] copyOf(short[] original, int newLength) {
        short[] copy = new short[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>0</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static int[] copyOf(int[] original, int newLength) {
        int[] copy = new int[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>0L</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static long[] copyOf(long[] original, int newLength) {
        long[] copy = new long[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with null characters (if necessary)
     * so the copy has the specified length.  For all indices that are valid
     * in both the original array and the copy, the two arrays will contain
     * identical values.  For any indices that are valid in the copy but not
     * the original, the copy will contain <tt>'\\u000'</tt>.  Such indices
     * will exist if and only if the specified length is greater than that of
     * the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with null characters
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>0f</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static float[] copyOf(float[] original, int newLength) {
        float[] copy = new float[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with zeros (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>0d</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with zeros
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static double[] copyOf(double[] original, int newLength) {
        double[] copy = new double[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified array, truncating or padding with <tt>false</tt> (if necessary)
     * so the copy has the specified length.  For all indices that are
     * valid in both the original array and the copy, the two arrays will
     * contain identical values.  For any indices that are valid in the
     * copy but not the original, the copy will contain <tt>false</tt>.
     * Such indices will exist if and only if the specified length
     * is greater than that of the original array.
     *
     * @param original  the array to be copied
     * @param newLength the length of the copy to be returned
     * @return a copy of the original array, truncated or padded with false elements
     * to obtain the specified length
     * @throws NegativeArraySizeException if <tt>newLength</tt> is negative
     * @throws NullPointerException       if <tt>original</tt> is null
     */
    public static boolean[] copyOf(boolean[] original, int newLength) {
        boolean[] copy = new boolean[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>null</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     * <p>
     * The resulting array is of exactly the same class as the original array.
     *
     * @param <T>      the class of the objects in the array
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with nulls to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] copyOfRange(T[] original, int from, int to) {
        return copyOfRange(original, from, to, (Class<? extends T[]>) original.getClass());
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>null</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     * The resulting array is of the class <tt>newType</tt>.
     *
     * @param <U>      the class of the objects in the original array
     * @param <T>      the class of the objects in the returned array
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @param newType  the class of the copy to be returned
     * @return a new array containing the specified range from the original array,
     * truncated or padded with nulls to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     * @throws ArrayStoreException            if an element copied from
     *                                        <tt>original</tt> is not of a runtime type that can be stored in
     *                                        an array of class <tt>newType</tt>.
     */
    public static <T, U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        T[] copy = ((Object) newType == (Object) Object[].class)
                   ? (T[]) new Object[newLength]
                   : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>(byte)0</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static byte[] copyOfRange(byte[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        byte[] copy = new byte[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>(short)0</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static short[] copyOfRange(short[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        short[] copy = new short[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>0</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static int[] copyOfRange(int[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        int[] copy = new int[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>0L</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static long[] copyOfRange(long[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        long[] copy = new long[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>'\\u000'</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with null characters to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static char[] copyOfRange(char[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        char[] copy = new char[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>0f</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static float[] copyOfRange(float[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        float[] copy = new float[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>0d</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with zeros to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static double[] copyOfRange(double[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        double[] copy = new double[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /**
     * Copies the specified range of the specified array into a new array.
     * The initial index of the range (<tt>from</tt>) must lie between zero
     * and <tt>original.length</tt>, inclusive.  The value at
     * <tt>original[from]</tt> is placed into the initial element of the copy
     * (unless <tt>from == original.length</tt> or <tt>from == to</tt>).
     * Values from subsequent elements in the original array are placed into
     * subsequent elements in the copy.  The final index of the range
     * (<tt>to</tt>), which must be greater than or equal to <tt>from</tt>,
     * may be greater than <tt>original.length</tt>, in which case
     * <tt>false</tt> is placed in all elements of the copy whose index is
     * greater than or equal to <tt>original.length - from</tt>.  The length
     * of the returned array will be <tt>to - from</tt>.
     *
     * @param original the array from which a range is to be copied
     * @param from     the initial index of the range to be copied, inclusive
     * @param to       the final index of the range to be copied, exclusive.
     *                 (This index may lie outside the array.)
     * @return a new array containing the specified range from the original array,
     * truncated or padded with false elements to obtain the required length
     * @throws ArrayIndexOutOfBoundsException if {@code from < 0}
     *                                        or {@code from > original.length}
     * @throws IllegalArgumentException       if <tt>from &gt; to</tt>
     * @throws NullPointerException           if <tt>original</tt> is null
     */
    public static boolean[] copyOfRange(boolean[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0) {throw new IllegalArgumentException(from + " > " + to);}
        boolean[] copy = new boolean[newLength];
        System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
        return copy;
    }
    
    /*****************************************************/
    // 对象或列表，转换成数组
    /*****************************************************/
    
    /**
     * 对一个未知的数组对象，查看数组长度
     *
     * @param obj 未知的数组对象
     * @return 数组长度
     */
    public static int length(Object obj) {
        if (!Types.isArray(obj)) {return 0;}
        return Array.getLength(obj);
    }
    
    /**
     * 对一个未知的数组对象，查看数组是否为空
     *
     * @param obj 未知的数组对象
     * @return 数组是否为空
     */
    public static boolean isEmpty(Object obj) {
        return length(obj) == 0;
    }
    
    /**
     * 对一个未知的数组对象，转化为一个已知的数组对象，其中对基本类型进行转化
     *
     * @param obj 未知的数组对象
     * @return 已知的数组对象
     */
    public static Object[] toArray(Object obj) {
        Asserts.as(Types.isArray(obj) ? null : "传入的参数不是数组类型");
        if (!Types.isPrimitiveArray(obj)) {return (Object[]) obj;}
        
        int len = length(obj);
        Class<?> clazz = Types.getWrapType(obj.getClass().getComponentType());
        Object[] objects = (Object[]) Array.newInstance(clazz, len);
        for (int i = 0; i < len; i++) {objects[i] = Array.get(obj, i);}
        return objects;
    }
    
    /**
     * 列表转数组，如果list=null，则返回null
     *
     * @param list  列表
     * @param clazz 类型
     * @return 指定类型的对象数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] toArray(List<T> list, Class<T> clazz) {
        if (list == null) {return null;}
        
        // 可能的基本类型转为对象类型，如int.class转为Integer.class
        clazz = (Class<T>) Types.getWrapType(clazz);
        return list.toArray((T[]) Array.newInstance(clazz, 0));
    }
    
    /**
     * 整型列表转为int[0]，而不是Integer[]，list=null返回int[0]
     *
     * @param list 列表
     * @return int[]
     */
    public static int[] toArrayInt(List<Integer> list) {
        if (list == null) {return new int[0];}
        
        return Arrays.convert(list.toArray(new Integer[0]));
    }
    
    /**
     * 整型列表转为long[]数组，而不是Long[]，list=null返回long[0]
     *
     * @param list 列表
     * @return long[]
     */
    public static long[] toArrayLong(List<Long> list) {
        if (list == null) {return new long[0];}
        
        return Arrays.convert(list.toArray(new Long[0]));
    }
    
    /**
     * 整型列表转为byte[]数组，而不是Byte[]，list=null返回byte[0]
     *
     * @param list 列表
     * @return long[]
     */
    public static byte[] toArrayByte(List<Byte> list) {
        if (list == null) {return new byte[0];}
        
        return Arrays.convert(list.toArray(new Byte[0]));
    }
    
    /**
     * 整型列表转为short[]数组，而不是Short[]，list=null返回short[0]
     *
     * @param list 列表
     * @return short[]
     */
    public static short[] toArrayShort(List<Short> list) {
        if (list == null) {return new short[0];}
        
        return Arrays.convert(list.toArray(new Short[0]));
    }
    
    /**
     * 整型列表转为boolean[]数组，而不是Boolean[]，list=null返回boolean[0]
     *
     * @param list 列表
     * @return boolean[]
     */
    public static boolean[] toArrayBoolean(List<Boolean> list) {
        if (list == null) {return new boolean[0];}
        
        return Arrays.convert(list.toArray(new Boolean[0]));
    }
    
    
    /**
     * 整型列表转为char[]数组，而不是Character[]，list=null返回char[0]
     *
     * @param list 列表
     * @return char[]
     */
    public static char[] toArrayChar(List<Character> list) {
        if (list == null) {return new char[0];}
        
        return Arrays.convert(list.toArray(new Character[0]));
    }
    
    /**
     * 整型列表转为float[]数组，而不是Float[]，list=null返回float[0]
     *
     * @param list 列表
     * @return float[]
     */
    public static float[] toArrayFloat(List<Float> list) {
        if (list == null) {return new float[0];}
        
        return Arrays.convert(list.toArray(new Float[0]));
    }
    
    /**
     * 整型列表转为double[]数组，而不是Double[]，list=null返回double[0]
     *
     * @param list 列表
     * @return double[]
     */
    public static double[] toArrayDouble(List<Double> list) {
        if (list == null) {return new double[0];}
        
        return Arrays.convert(list.toArray(new Double[0]));
    }
    
    /**
     * 整型列表转为String[]数组，而不是Boolean[]，list=null返回String[0]
     *
     * @param list 列表
     * @return boolean[]
     */
    public static String[] toArrayString(List<String> list) {
        String[] arr = new String[0];
        if (list == null) {return arr;}
        
        return list.toArray(new String[0]);
    }
    /*****************************************************/
    // 基本类型数组和基本对象类型数组相互转换
    /*****************************************************/
    
    /**
     * 布尔型对象相互转换，由Boolean[]转为boolean[]
     *
     * @param values Boolean[]
     * @return boolean[]
     */
    public static boolean[] convert(Boolean[] values) {
        if (values == null) {return null;}
        
        boolean[] results = new boolean[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 布尔型对象相互转换，由boolean[]转为Boolean[]
     *
     * @param values boolean[]
     * @return Boolean[]
     */
    public static Boolean[] convert(boolean[] values) {
        if (values == null) {return null;}
        
        Boolean[] results = new Boolean[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 字节型对象相互转换，由Byte[]转为byte[]
     *
     * @param values Byte[]
     * @return byte[]
     */
    public static byte[] convert(Byte[] values) {
        if (values == null) {return null;}
        
        byte[] results = new byte[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 字节型对象相互转换，由byte[]转为Byte[]
     *
     * @param values byte[]
     * @return Byte[]
     */
    public static Byte[] convert(byte[] values) {
        if (values == null) {return null;}
        Byte[] results = new Byte[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 字符型对象相互转换，由Character[]转为char[]
     *
     * @param values Character[]
     * @return char[]
     */
    public static char[] convert(Character[] values) {
        if (values == null) {return null;}
        
        char[] results = new char[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 字节型对象相互转换，由char[]转为Character[]
     *
     * @param values char[]
     * @return Character[]
     */
    public static Character[] convert(char[] values) {
        if (values == null) {return null;}
        
        Character[] results = new Character[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 短整型对象相互转换，由Short[]转为short[]
     *
     * @param values Short[]
     * @return short[]
     */
    public static short[] convert(Short[] values) {
        if (values == null) {return null;}
        
        short[] results = new short[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 短整型对象相互转换，由short[]转为Short[]
     *
     * @param values short[]
     * @return Short[]
     */
    public static Short[] convert(short[] values) {
        if (values == null) {return null;}
        
        Short[] results = new Short[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 整型对象相互转换，由Integer[]转为int[]
     *
     * @param values Integer[]
     * @return int[]
     */
    public static int[] convert(Integer[] values) {
        if (values == null) {return null;}
        
        int[] results = new int[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 整型对象相互转换，由int[]转为Integer[]
     *
     * @param values int[]
     * @return Integer[]
     */
    public static Integer[] convert(int[] values) {
        if (values == null) {return null;}
        
        Integer[] results = new Integer[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 长整型对象相互转换，由Long[]转为long[]
     *
     * @param values Long[]
     * @return long[]
     */
    public static long[] convert(Long[] values) {
        if (values == null) {return null;}
        
        long[] results = new long[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 长整型对象相互转换，由long[]转为Long[]
     *
     * @param values long[]
     * @return Long[]
     */
    public static Long[] convert(long[] values) {
        if (values == null) {return null;}
        
        Long[] results = new Long[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 浮点型对象相互转换，由Float[]转为float[]
     *
     * @param values Float[]
     * @return float[]
     */
    public static float[] convert(Float[] values) {
        if (values == null) {return null;}
        
        float[] results = new float[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 浮点型对象相互转换，由float[]转为Float[]
     *
     * @param values float[]
     * @return Float[]
     */
    public static Float[] convert(float[] values) {
        if (values == null) {return null;}
        
        Float[] results = new Float[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 双浮点型对象相互转换，由Double[]转为double[]
     *
     * @param values Double[]
     * @return double[]
     */
    public static double[] convert(Double[] values) {
        if (values == null) {return null;}
        
        double[] results = new double[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /**
     * 双浮点型对象相互转换，由double[]转为Double[]
     *
     * @param values double[]
     * @return Double[]
     */
    public static Double[] convert(double[] values) {
        if (values == null) {return null;}
        
        Double[] results = new Double[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = values[i];}
        
        return results;
    }
    
    /*******************************************/
    // 字符串转换为基本类型数组
    /*******************************************/
    
    /**
     * 把字符串按split分隔转换成字符串数组，注意每个字符串都将trim()操作去除前后空字符串(空格,\r,\n,\t,\b等)
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return String[] 转换后的去除前后空格的字符串数组(空格,\r,\n,\t,\b等)
     */
    public static String[] toStringArray(String strs, String separator) {
        if (Validates.isEmpty(strs)) {return new String[0];}
        
        String[] values = strs.split(separator);
        for (int i = 0; i < values.length; i++) {values[i] = StrUtils.trim(values[i]);}
        return values;
    }
    
    /**
     * 将数组转换成字符串，形成：[1,2,3,...] 形式
     *
     * @param obj 数组对象
     */
    public static String toString(Object obj) {
        if (null == obj) {
            return null;
        }
        
        if (obj instanceof long[]) {
            return java.util.Arrays.toString((long[]) obj);
        }
        else if (obj instanceof int[]) {
            return java.util.Arrays.toString((int[]) obj);
        }
        else if (obj instanceof short[]) {
            return java.util.Arrays.toString((short[]) obj);
        }
        else if (obj instanceof char[]) {
            return java.util.Arrays.toString((char[]) obj);
        }
        else if (obj instanceof byte[]) {
            return java.util.Arrays.toString((byte[]) obj);
        }
        else if (obj instanceof boolean[]) {
            return java.util.Arrays.toString((boolean[]) obj);
        }
        else if (obj instanceof float[]) {
            return java.util.Arrays.toString((float[]) obj);
        }
        else if (obj instanceof double[]) {
            return java.util.Arrays.toString((double[]) obj);
        }
        else if (obj != null || obj.getClass().isArray()) {
            // 对象数组
            try {
                return java.util.Arrays.deepToString((Object[]) obj);
            }
            catch (Exception ignore) {
                // ignore
            }
        }
        
        return obj.toString();
    }
    
    /**
     * 把字符串按逗号分隔转换成字符串数组，注意每个字符串都将trim()操作去除前后空字符串(空格,\r,\n,\t,\b等)
     *
     * @param obj Object数组
     * @return String[] 转换后的去除前后空格的字符串数组(空格,\r,\n,\t,\b等)
     */
    public static String toString(Object[] obj) {
        return toString(obj, ",");
    }
    
    /**
     * 把字符串按[逗号]分隔转换成字符串数组，注意每个字符串都将trim()操作去除前后空字符串(空格,\r,\n,\t,\b等)
     *
     * @param obj Object数组
     * @return String[] 转换后的去除前后空格的字符串数组(空格,\r,\n,\t,\b等)
     */
    public static String toString(Object[] obj, String separator) {
        if (obj == null) {return "null";}
        
        if (!obj.getClass().isArray()) {
            throw new IllegalArgumentException("非数组类型不允许调用Arrays.toString(Object o)");
        }
        return toStringCore(obj, separator);
    }
    
    /**
     * 把字符串按[逗号]分隔转换成字符串数组，注意每个字符串都将trim()操作去除前后空字符串(空格,\r,\n,\t,\b等)
     *
     * @param str 字符串
     * @return String[] 转换后的去除前后空格的字符串数组(空格,\r,\n,\t,\b等)
     */
    public static String[] toStringArray(String str) {
        return toStringArray(str, ",");
    }
    
    /**
     * 把对象数组转为字符串数组，对内部所有元素作String.valueOf()操作
     *
     * @param objs 对象数组
     * @return String[] 转换后的去除前后空格的字符串数组(空格,\r,\n,\t,\b等)
     */
    public static String[] toStringArray(Object[] objs) {
        if (objs == null) {return new String[0];}
        
        String[] strs = new String[objs.length];
        for (int i = 0; i < objs.length; i++) {
            Object target = objs[i];
            strs[i] = target == null ? null : StrUtils.trim(String.valueOf(target));
        }
        
        return strs;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成布尔型数组
     *
     * @param strs 字符串
     * @return boolean[] 转换后的如果字符串内容为true的则boolean值为true，否则为false
     */
    public static boolean[] toBooleanArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toBooleanArray(values);
    }
    
    /**
     * 把字符串按分隔符转换成布尔型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return boolean[] 转换后的如果字符串内容为true的则boolean值为true，否则为false
     */
    public static boolean[] toBooleanArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toBooleanArray(values);
    }
    
    /**
     * 字符串数组转换成布尔型数组
     *
     * @param values 字符串数组
     * @return boolean[] 转换后的如果字符串内容为true的则boolean值为true，否则为false
     */
    public static boolean[] toBooleanArray(String[] values) {
        boolean[] results = new boolean[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = "true".equals(StrUtils.trim(values[i]));}
        
        return results;
    }
    
    /**
     * 把字符串按分隔符转换成布尔型数组，支持值为1时也为true
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return boolean[] 转换后的如果字符串内容为true或1的则boolean值为true，否则为false
     */
    public static boolean[] toBooleanArrayForm(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        boolean[] results = new boolean[values.length];
        for (int i = 0; i < values.length; i++) {
            results[i] = "true".equals(StrUtils.trim(values[i])) || "1".equals(StrUtils.trim(values[i]));
        }
        
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成byte数组
     *
     * @param strs 字符串
     * @return int[] 转换后的byte数组
     */
    public static byte[] toByteArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toByteArray(values);
    }
    
    /**
     * 把字符串按分隔符分隔转换成byte数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return int[]    转换后的byte数组
     */
    public static byte[] toByteArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toByteArray(values);
    }
    
    /**
     * 把字符串数组传换成byte数组
     *
     * @param values 字符串数组
     * @return int[] 转换后的byte数组
     */
    public static byte[] toByteArray(String[] values) {
        
        byte[] results = new byte[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Byte.parseByte(StrUtils.trim(values[i]));}
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成短整型数组
     *
     * @param strs 字符串
     * @return short[]
     */
    public static short[] toShortArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toShortArray(values);
    }
    
    /**
     * 把字符串按分隔符转换成短整型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return short[]
     */
    public static short[] toShortArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toShortArray(values);
    }
    
    /**
     * 字符串数组转换成短整型数组
     *
     * @param values 字符串数组
     * @return short[]
     */
    public static short[] toShortArray(String[] values) {
        short[] results = new short[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Short.parseShort(StrUtils.trim(values[i]));}
        
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成整型数组
     *
     * @param strs 字符串
     * @return int[] 转换后的整数数组
     */
    public static int[] toIntArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toIntArray(values);
    }
    
    /**
     * 把字符串按分隔符分隔转换成整型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return int[]    转换后的整数数组
     */
    public static int[] toIntArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toIntArray(values);
    }
    
    /**
     * 把字符串数组传换成整型数组
     *
     * @param values 字符串数组
     * @return int[] 转换后的整型数组
     */
    public static int[] toIntArray(String[] values) {
        int[] results = new int[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Integer.parseInt(StrUtils.trim(values[i]));}
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成长整型数组
     *
     * @param strs 字符串
     * @return long[]   转换后的整数数组
     */
    public static long[] toLongArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toLongArray(values);
    }
    
    /**
     * 把字符串按分隔符分隔转换成长整型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return long[]   转换后的整数数组
     */
    public static long[] toLongArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toLongArray(values);
    }
    
    /**
     * 把字符串数组传换成长整型数组
     *
     * @param values 字符串数组
     * @return long[]   转换后的长整型数组
     */
    public static long[] toLongArray(String[] values) {
        long[] results = new long[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Long.parseLong(StrUtils.trim(values[i]));}
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成浮点型数组
     *
     * @param strs 字符串
     * @return float[]  转换后的浮点型数组
     */
    public static float[] toFloatArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toFloatArray(values);
    }
    
    /**
     * 把字符串按分隔符分隔转换成浮点型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return float[]  转换后的浮点型数组
     */
    public static float[] toFloatArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toFloatArray(values);
    }
    
    /**
     * 把字符串数组传换成浮点型数组
     *
     * @param values 字符串数组
     * @return float[]  转换后的浮点型数组
     */
    public static float[] toFloatArray(String[] values) {
        float[] results = new float[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Float.parseFloat(StrUtils.trim(values[i]));}
        return results;
    }
    
    /**
     * 把字符串按[逗号]分隔转换成双浮点型数组
     *
     * @param strs 字符串
     * @return double[] 转换后的双浮点型数组
     */
    public static double[] toDoubleArray(String strs) {
        String[] values = toStringArray(strs, ",");
        return toDoubleArray(values);
    }
    
    /**
     * 把字符串按分隔符分隔转换成双浮点型数组
     *
     * @param strs      字符串
     * @param separator 分隔符
     * @return double[] 转换后的双浮点型数组
     */
    public static double[] toDoubleArray(String strs, String separator) {
        String[] values = toStringArray(strs, separator);
        return toDoubleArray(values);
    }
    
    /**
     * 把字符串数组传换成双浮点型数组
     *
     * @param values 字符串数组
     * @return double[] 转换后的双浮点型数组
     */
    public static double[] toDoubleArray(String[] values) {
        double[] results = new double[values.length];
        for (int i = 0; i < values.length; i++) {results[i] = Double.parseDouble(StrUtils.trim(values[i]));}
        return results;
    }
    
    /*******************************************/
    // 基本类型数组转换为字符串
    /*******************************************/
    
    /**
     * 数组转换成分隔符分隔的字符串
     *
     * @param values    字符串数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 abc,def,pdf
     */
    public static String toString(String[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 数组转换成[逗号]分隔的字符串
     *
     * @param values 字符串数组
     * @return 分隔符分隔的字符串, 如 abc,def,pdf
     */
    public static String toString(String[] values) {
        return toString(values, ",");
    }
    
    /**
     * 布尔型数组转换成分隔符分隔的字符串
     *
     * @param values    布尔型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(boolean[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 布尔型数组转换成[逗号]分隔的字符串
     *
     * @param values 布尔型数组
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(boolean[] values) {
        return toString(values, ",");
    }
    
    /**
     * byte型数组转换成分隔符分隔的字符串
     *
     * @param values    短整型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(byte[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * byte整型数组转换成[逗号]分隔的字符串
     *
     * @param values 短整型数组
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(byte[] values) {
        return toString(values, ",");
    }
    
    /**
     * 短整型数组转换成分隔符分隔的字符串
     *
     * @param values    短整型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(short[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 短整型数组转换成[逗号]分隔的字符串
     *
     * @param values 短整型数组
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(short[] values) {
        return toString(values, ",");
    }
    
    /**
     * 数组转换成分隔符分隔的字符串
     *
     * @param values    整型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(int[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 整型数组转换成分隔符分隔的字符串
     *
     * @param values 整型数组
     * @return 分隔符分隔的字符串, 如 12,1,34
     */
    public static String toString(int[] values) {
        return toString(values, ",");
    }
    
    /**
     * 长整型数组转换成分隔符分隔的字符串
     *
     * @param values    长整型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 1222233,232323,2346777
     */
    public static String toString(long[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 长整型数组转换成[逗号]分隔的字符串
     *
     * @param values 长整型数组
     * @return 分隔符分隔的字符串, 如 1222233,232323,2346777
     */
    public static String toString(long[] values) {
        return toString(values, ",");
    }
    
    /**
     * 浮点型数组转换成分隔符分隔的字符串
     *
     * @param values    浮点型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 12222.33,232.323,23.46777
     */
    public static String toString(float[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 浮点型数组转换成[逗号]分隔的字符串
     *
     * @param values 浮点型数组
     * @return 分隔符分隔的字符串, 如 12222.33,232.323,23.46777
     */
    public static String toString(float[] values) {
        return toString(values, ",");
    }
    
    /**
     * 双浮点型数组转换成分隔符分隔的字符串
     *
     * @param values    双浮点型数组
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 如 1222.233,23.2323,2.346777
     */
    public static String toString(double[] values, String separator) {
        return toStringCore(values, separator);
    }
    
    /**
     * 双浮点型数组转换成[逗号]分隔的字符串
     *
     * @param values 双浮点型数组
     * @return 分隔符分隔的字符串, 如 1222.233,23.2323,2.346777
     */
    public static String toString(double[] values) {
        return toString(values, ",");
    }
    
    private static String toStringCore(Object objs, String separator) {
        int len = length(objs);
        if (len == 0) {return _EMPTY_;}
        StringBuilder strb = new StringBuilder().append(Array.get(objs, 0));
        for (int i = 1; i < len; i++) {strb.append(separator).append(Array.get(objs, i));}
        return strb.toString();
    }
    
    /**
     * 数组转换成分隔符分隔的字符串，每个子项用''处理成SQL支持的格式，子项有'的进行''转义
     *
     * @param values    字符串数组String[]{"abc", "de''f", "p'df"}
     * @param separator 分隔符
     * @return 分隔符分隔的字符串, 转化为str = "'abc','de''''f','p''df'"
     */
    public static String toStringSql(String[] values, String separator) {
        if (values == null || values.length == 0) {return _EMPTY_;}
        
        String[] strSqlArr = new String[values.length];
        for (int i = 0; i < values.length; i++) {
            strSqlArr[i] = Sqls.formatQuote(values[i]);
        }
        
        StringBuilder strb = new StringBuilder();
        strb.append("'").append(strSqlArr[0]).append("'");
        for (int i = 1; i < values.length; i++) {
            strb.append(separator).append("'").append(values[i]).append("'");
        }
        
        return strb.toString();
    }
    
    /**
     * 过滤相同的字段
     *
     * @param values 数组
     * @return 过滤后的数组
     */
    public static String[] toFilterSame(String[] values) {
        HashSet<String> strSet = toFilterSameSet(values);
        if (strSet.isEmpty()) {return new String[0];}
        
        String[] dStrArr = new String[strSet.size()];
        int i = 0;
        for (String str : strSet) {
            dStrArr[i++] = str;
        }
        
        return dStrArr;
    }
    
    /**
     * 过滤相同的字段，返回separator隔开的字符串
     *
     * @param strs 逗号分隔的字符串,如 1222.233,23.2323,2.346777
     * @return 过滤后separator隔开的字符串
     */
    public static String toFilterSameStr(String strs) {
        return toFilterSameStr(toStringArray(strs, ","), ",");
    }
    
    /**
     * 过滤相同的字段，返回separator隔开的字符串
     *
     * @param strs      分隔符分隔的字符串,如 1222.233,23.2323,2.346777
     * @param separator 分隔符
     * @return 过滤后separator隔开的字符串
     */
    public static String toFilterSameStr(String strs, String separator) {
        return toFilterSameStr(toStringArray(strs, separator), separator);
    }
    
    /**
     * 过滤相同的字段，返回separator隔开的字符串
     *
     * @param values    数组
     * @param separator 分隔符
     * @return 返回separator隔开的字符串
     */
    public static String toFilterSameStr(String[] values, String separator) {
        List<String> strList = toFilterSameList(values);
        if (strList.isEmpty()) {return _EMPTY_;}
        
        StringBuilder strb = new StringBuilder().append(strList.get(0));
        for (int i = 1; i < strList.size(); i++) {
            strb.append(separator).append(strList.get(i));
        }
        return strb.toString();
    }
    
    /**
     * 过滤相同的字段，返回逗号隔开的字符串
     *
     * @param values 数组
     * @return 返回逗号隔开的字符串
     */
    public static String toFilterSameStr(String[] values) {
        return toFilterSameStr(values, ",");
    }
    
    /**
     * 过滤相同的字段
     *
     * @param values 数组
     * @return 过滤后的集合
     */
    public static HashSet<String> toFilterSameSet(String[] values) {
        if (values == null || values.length == 0) {return new HashSet<String>(0);}
        
        HashSet<String> strSet = new HashSet<String>();
        for (String str : values) {strSet.add(StrUtils.trim(str));}
        
        return strSet;
    }
    
    /**
     * 过滤相同的字段
     *
     * @param values 数组
     * @return 过滤后的列表
     */
    public static String[] toFilterSameList(List<String> values) {
        if (values == null || values.isEmpty()) {return new String[0];}
        
        LinkedHashSet<String> strSet = new LinkedHashSet<String>(values.size());
        for (String str : values) {
            strSet.add(StrUtils.trim(str));
        }
        
        return strSet.toArray(new String[0]);
    }
    
    /**
     * 过滤相同的字段
     *
     * @param values 数组
     * @return 过滤后的列表
     */
    public static List<String> toFilterSameList(String[] values) {
        if (values == null || values.length == 0) {return new ArrayList<String>();}
        Set<String> set = new LinkedHashSet<>();
        for (String str : values) {set.add(StrUtils.trim(str));}
        return new ArrayList<String>(set);
    }
    
    /**
     * Returns a sequential {@link Stream} with the specified array as its
     * source.
     *
     * @param <T>   The type of the array elements
     * @param array The array, assumed to be unmodified during use
     * @return a {@code Stream} for the array
     * 1.8
     */
    public static <T> Stream<T> stream(T[] array) {
        return stream(array, 0, array.length);
    }
    
    /**
     * Returns a sequential {@link Stream} with the specified range of the
     * specified array as its source.
     *
     * @param <T>            the type of the array elements
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a {@code Stream} for the array range
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) {
        return StreamSupport.stream(spliterator(array, startInclusive, endExclusive), false);
    }
    
    
    /**
     * Returns a sequential {@link IntStream} with the specified array as its
     * source.
     *
     * @param array the array, assumed to be unmodified during use
     * @return an {@code IntStream} for the array
     */
    public static IntStream stream(int[] array) {
        return stream(array, 0, array.length);
    }
    
    /**
     * Returns a sequential {@link IntStream} with the specified range of the
     * specified array as its source.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return an {@code IntStream} for the array range
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static IntStream stream(int[] array, int startInclusive, int endExclusive) {
        return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive), false);
    }
    
    /**
     * Returns a {@link Spliterator} covering all of the specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param <T>   type of elements
     * @param array the array, assumed to be unmodified during use
     * @return a spliterator for the array elements
     * 1.8
     */
    public static <T> Spliterator<T> spliterator(T[] array) {
        return Spliterators.spliterator(array,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a {@link Spliterator} covering the specified range of the
     * specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param <T>            type of elements
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a spliterator for the array elements
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static <T> Spliterator<T> spliterator(T[] array, int startInclusive, int endExclusive) {
        return Spliterators.spliterator(array,
                                        startInclusive,
                                        endExclusive,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a {@link Spliterator.OfInt} covering the specified range of the
     * specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a spliterator for the array elements
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) {
        return Spliterators.spliterator(array, startInclusive, endExclusive,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    
    /**
     * Returns a {@link Spliterator.OfLong} covering all of the specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param array the array, assumed to be unmodified during use
     * @return the spliterator for the array elements
     * 1.8
     */
    public static Spliterator.OfLong spliterator(long[] array) {
        return Spliterators.spliterator(array,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a {@link Spliterator.OfLong} covering the specified range of the
     * specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a spliterator for the array elements
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static Spliterator.OfLong spliterator(long[] array, int startInclusive, int endExclusive) {
        return Spliterators.spliterator(array, startInclusive, endExclusive,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a {@link Spliterator.OfDouble} covering all of the specified
     * array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param array the array, assumed to be unmodified during use
     * @return a spliterator for the array elements
     * 1.8
     */
    public static Spliterator.OfDouble spliterator(double[] array) {
        return Spliterators.spliterator(array,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a {@link Spliterator.OfDouble} covering the specified range of
     * the specified array.
     *
     * <p>The spliterator reports {@link Spliterator#SIZED},
     * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
     * {@link Spliterator#IMMUTABLE}.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a spliterator for the array elements
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static Spliterator.OfDouble spliterator(double[] array, int startInclusive, int endExclusive) {
        return Spliterators.spliterator(array, startInclusive, endExclusive,
                                        Spliterator.ORDERED | Spliterator.IMMUTABLE);
    }
    
    /**
     * Returns a sequential {@link LongStream} with the specified array as its
     * source.
     *
     * @param array the array, assumed to be unmodified during use
     * @return a {@code LongStream} for the array
     * 1.8
     */
    public static LongStream stream(long[] array) {
        return stream(array, 0, array.length);
    }
    
    /**
     * Returns a sequential {@link LongStream} with the specified range of the
     * specified array as its source.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a {@code LongStream} for the array range
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static LongStream stream(long[] array, int startInclusive, int endExclusive) {
        return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive), false);
    }
    
    
    /**
     * Returns a sequential {@link DoubleStream} with the specified array as its
     * source.
     *
     * @param array the array, assumed to be unmodified during use
     * @return a {@code DoubleStream} for the array
     * 1.8
     */
    public static DoubleStream stream(double[] array) {
        return stream(array, 0, array.length);
    }
    
    /**
     * Returns a sequential {@link DoubleStream} with the specified range of the
     * specified array as its source.
     *
     * @param array          the array, assumed to be unmodified during use
     * @param startInclusive the first index to cover, inclusive
     * @param endExclusive   index immediately past the last index to cover
     * @return a {@code DoubleStream} for the array range
     * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is
     *                                        negative, {@code endExclusive} is less than
     *                                        {@code startInclusive}, or {@code endExclusive} is greater than
     *                                        the array size
     *                                        1.8
     */
    public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) {
        return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false);
    }
    
    
    // ------------------------------------------------------------------------------------------------------------ min and max
    
    /**
     * 取最小值
     *
     * @param <T>         元素类型
     * @param numberArray 数字数组
     * @return 最小值
     */
    public static <T extends Comparable<? super T>> T min(T[] numberArray) {
        return min(numberArray, null);
    }
    
    /**
     * 取最小值
     *
     * @param <T>         元素类型
     * @param numberArray 数字数组
     * @param comparator  比较器，null按照默认比较
     * @return 最小值
     */
    public static <T extends Comparable<? super T>> T min(T[] numberArray, Comparator<T> comparator) {
        if (isEmpty(numberArray)) {
            throw new IllegalArgumentException("Number array must not empty !");
        }
        T min = numberArray[0];
        for (T t : numberArray) {
            if (CompareUtil.compare(min, t, comparator) > 0) {
                min = t;
            }
        }
        return min;
    }
    
    /**
     * 取最大值
     *
     * @param <T>         元素类型
     * @param numberArray 数字数组
     * @return 最大值
     */
    public static <T extends Comparable<? super T>> T max(T[] numberArray) {
        return max(numberArray, null);
    }
    
    /**
     * 取最大值
     *
     * @param <T>         元素类型
     * @param numberArray 数字数组
     * @param comparator  比较器，null表示默认比较器
     * @return 最大值
     */
    public static <T extends Comparable<? super T>> T max(T[] numberArray, Comparator<T> comparator) {
        if (isEmpty(numberArray)) {
            throw new IllegalArgumentException("Number array must not empty !");
        }
        T max = numberArray[0];
        for (int i = 1; i < numberArray.length; i++) {
            if (CompareUtil.compare(max, numberArray[i], comparator) < 0) {
                max = numberArray[i];
            }
        }
        return max;
    }
    
    //----------------------------------------------------------------
    //---------------------------------------------------------------- contains
    //----------------------------------------------------------------
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final boolean[] array, final boolean valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(byte[])} and {@link Arrays#binarySearch(byte[], byte)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final byte[] array, final byte valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(char[])} and {@link Arrays#binarySearch(char[], char)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     * @since 2.1
     */
    public static boolean contains(final char[] array, final char valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(double[])} and {@link Arrays#binarySearch(double[], double)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final double[] array, final double valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if a value falling within the given tolerance is in the
     * given array.  If the array contains a value within the inclusive range
     * defined by (value - tolerance) to (value + tolerance).
     * <p>
     * The method returns {@code false} if a {@code null} array
     * is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(double[])} and {@link Arrays#binarySearch(double[], double)}.
     * </p>
     *
     * @param array       the array to search
     * @param valueToFind the value to find
     * @param tolerance   the array contains the tolerance of the search
     * @return true if value falling within tolerance is in array
     */
    public static boolean contains(final double[] array, final double valueToFind, final double tolerance) {
        return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(float[])} and {@link Arrays#binarySearch(float[], float)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final float[] array, final float valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(int[])} and {@link Arrays#binarySearch(int[], int)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final int[] array, final int valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(long[])} and {@link Arrays#binarySearch(long[], long)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final long[] array, final long valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the object is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(Object[], Comparator)} and {@link Arrays#binarySearch(Object[], Object)}.
     * </p>
     *
     * @param array        the array to search through
     * @param objectToFind the object to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final Object[] array, final Object objectToFind) {
        return indexOf(array, objectToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if the value is in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(short[])} and {@link Arrays#binarySearch(short[], short)}.
     * </p>
     *
     * @param array       the array to search through
     * @param valueToFind the value to find
     * @return {@code true} if the array contains the object
     */
    public static boolean contains(final short[] array, final short valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }
    
    /**
     * Checks if any of the ints are in the given array.
     * <p>
     * The method returns {@code false} if a {@code null} array is passed in.
     * </p>
     * <p>
     * If the {@code array} elements you are searching implement {@link Comparator}, consider whether it is worth using
     * {@link Arrays#sort(int[])} and {@link Arrays#binarySearch(int[], int)}.
     * </p>
     *
     * @param array         the array to search through
     * @param objectsToFind any of the ints to find
     * @return {@code true} if the array contains any of the ints
     * @since 3.18.0
     */
    public static boolean containsAny(final int[] array, final int... objectsToFind) {
        return IntStreams.of(objectsToFind).anyMatch(e -> contains(array, e));
    }
    
    
    //----------------------------------------------------------------
    //---------------------------------------------------------------- indexOf
    //----------------------------------------------------------------
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final boolean[] array, final boolean valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null}
     * array input
     */
    public static int indexOf(final boolean[] array, final boolean valueToFind, final int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final byte[] array, final byte valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final byte[] array, final byte valueToFind, final int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     * @since 2.1
     */
    public static int indexOf(final char[] array, final char valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     * @since 2.1
     */
    public static int indexOf(final char[] array, final char valueToFind, final int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final double[] array, final double valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value within a given tolerance in the array.
     * This method will return the index of the first value which falls between the region
     * defined by valueToFind - tolerance and valueToFind + tolerance.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param tolerance   tolerance of the search
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final double[] array, final double valueToFind, final double tolerance) {
        return indexOf(array, valueToFind, 0, tolerance);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final double[] array, final double valueToFind, final int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        final boolean searchNaN = Double.isNaN(valueToFind);
        for (int i = max0(startIndex); i < array.length; i++) {
            final double element = array[i];
            if (valueToFind == element || searchNaN && Double.isNaN(element)) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * This method will return the index of the first value which falls between the region
     * defined by valueToFind - tolerance and valueToFind + tolerance.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @param tolerance   tolerance of the search
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final double[] array, final double valueToFind, final int startIndex,
                              final double tolerance) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        final double min = valueToFind - tolerance;
        final double max = valueToFind + tolerance;
        for (int i = max0(startIndex); i < array.length; i++) {
            if (array[i] >= min && array[i] <= max) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final float[] array, final float valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final float[] array, final float valueToFind, final int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        final boolean searchNaN = Float.isNaN(valueToFind);
        for (int i = max0(startIndex); i < array.length; i++) {
            final float element = array[i];
            if (valueToFind == element || searchNaN && Float.isNaN(element)) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final int[] array, final int valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final int[] array, final int valueToFind, final int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null}
     * array input
     */
    public static int indexOf(final long[] array, final long valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final long[] array, final long valueToFind, final int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given object in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array        the array to search through for the object, may be {@code null}
     * @param objectToFind the object to find, may be {@code null}
     * @return the index of the object within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final Object[] array, final Object objectToFind) {
        return indexOf(array, objectToFind, 0);
    }
    
    /**
     * Finds the index of the given object in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array        the array to search through for the object, may be {@code null}
     * @param objectToFind the object to find, may be {@code null}
     * @param startIndex   the index to start searching at
     * @return the index of the object within the array starting at the index,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final Object[] array, final Object objectToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        startIndex = max0(startIndex);
        if (objectToFind == null) {
            for (int i = startIndex; i < array.length; i++) {
                if (array[i] == null) {
                    return i;
                }
            }
        }
        else {
            for (int i = startIndex; i < array.length; i++) {
                if (objectToFind.equals(array[i])) {
                    return i;
                }
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    /**
     * Finds the index of the given value in the array.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final short[] array, final short valueToFind) {
        return indexOf(array, valueToFind, 0);
    }
    
    /**
     * Finds the index of the given value in the array starting at the given index.
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.
     * </p>
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the array
     * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).
     * </p>
     *
     * @param array       the array to search through for the object, may be {@code null}
     * @param valueToFind the value to find
     * @param startIndex  the index to start searching at
     * @return the index of the value within the array,
     * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
     */
    public static int indexOf(final short[] array, final short valueToFind, final int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        for (int i = max0(startIndex); i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }
    
    //----------------------------------------------------------------
    //---------------------------------------------------------------- Searching
    //----------------------------------------------------------------
    
    /**
     * Searches the specified array of longs for the specified value using the
     * binary search algorithm.  The array must be sorted (as
     * by the {@link #sort(long[])} method) prior to making this call.  If it
     * is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(long[] a, long key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of longs for the specified value using the
     * binary search algorithm.
     * The range must be sorted (as
     * by the {@link #sort(long[], int, int)} method)
     * prior to making this call.  If it
     * is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(long[] a, int fromIndex, int toIndex,
                                   long key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(long[] a, int fromIndex, int toIndex,
                                     long key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            long midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of ints for the specified value using the
     * binary search algorithm.  The array must be sorted (as
     * by the {@link #sort(int[])} method) prior to making this call.  If it
     * is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of ints for the specified value using the
     * binary search algorithm.
     * The range must be sorted (as
     * by the {@link #sort(int[], int, int)} method)
     * prior to making this call.  If it
     * is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(int[] a, int fromIndex, int toIndex,
                                   int key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of shorts for the specified value using
     * the binary search algorithm.  The array must be sorted
     * (as by the {@link #sort(short[])} method) prior to making this call.  If
     * it is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(short[] a, short key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of shorts for the specified value using
     * the binary search algorithm.
     * The range must be sorted
     * (as by the {@link #sort(short[], int, int)} method)
     * prior to making this call.  If
     * it is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(short[] a, int fromIndex, int toIndex,
                                   short key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(short[] a, int fromIndex, int toIndex,
                                     short key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            short midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of chars for the specified value using the
     * binary search algorithm.  The array must be sorted (as
     * by the {@link #sort(char[])} method) prior to making this call.  If it
     * is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(char[] a, char key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of chars for the specified value using the
     * binary search algorithm.
     * The range must be sorted (as
     * by the {@link #sort(char[], int, int)} method)
     * prior to making this call.  If it
     * is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(char[] a, int fromIndex, int toIndex,
                                   char key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(char[] a, int fromIndex, int toIndex,
                                     char key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            char midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of bytes for the specified value using the
     * binary search algorithm.  The array must be sorted (as
     * by the {@link #sort(byte[])} method) prior to making this call.  If it
     * is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(byte[] a, byte key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of bytes for the specified value using the
     * binary search algorithm.
     * The range must be sorted (as
     * by the {@link #sort(byte[], int, int)} method)
     * prior to making this call.  If it
     * is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(byte[] a, int fromIndex, int toIndex,
                                   byte key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(byte[] a, int fromIndex, int toIndex,
                                     byte key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            byte midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of doubles for the specified value using
     * the binary search algorithm.  The array must be sorted
     * (as by the {@link #sort(double[])} method) prior to making this call.
     * If it is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.  This method considers all NaN values to be
     * equivalent and equal.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(double[] a, double key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of doubles for the specified value using
     * the binary search algorithm.
     * The range must be sorted
     * (as by the {@link #sort(double[], int, int)} method)
     * prior to making this call.
     * If it is not sorted, the results are undefined.  If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.  This method considers all NaN values to be
     * equivalent and equal.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(double[] a, int fromIndex, int toIndex,
                                   double key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(double[] a, int fromIndex, int toIndex,
                                     double key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            double midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;  // Neither val is NaN, thisVal is smaller
            else if (midVal > key)
                high = mid - 1; // Neither val is NaN, thisVal is larger
            else {
                long midBits = Double.doubleToLongBits(midVal);
                long keyBits = Double.doubleToLongBits(key);
                if (midBits == keyBits)     // Values are equal
                    return mid;             // Key found
                else if (midBits < keyBits) // (-0.0, 0.0) or (!NaN, NaN)
                    low = mid + 1;
                else                        // (0.0, -0.0) or (NaN, !NaN)
                    high = mid - 1;
            }
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array of floats for the specified value using
     * the binary search algorithm. The array must be sorted
     * (as by the {@link #sort(float[])} method) prior to making this call. If
     * it is not sorted, the results are undefined. If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found. This method considers all NaN values to be
     * equivalent and equal.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key. Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(float[] a, float key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array of floats for the specified value using
     * the binary search algorithm.
     * The range must be sorted
     * (as by the {@link #sort(float[], int, int)} method)
     * prior to making this call. If
     * it is not sorted, the results are undefined. If the range contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found. This method considers all NaN values to be
     * equivalent and equal.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key. Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(float[] a, int fromIndex, int toIndex,
                                   float key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(float[] a, int fromIndex, int toIndex,
                                     float key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            float midVal = a[mid];
            
            if (midVal < key)
                low = mid + 1;  // Neither val is NaN, thisVal is smaller
            else if (midVal > key)
                high = mid - 1; // Neither val is NaN, thisVal is larger
            else {
                int midBits = Float.floatToIntBits(midVal);
                int keyBits = Float.floatToIntBits(key);
                if (midBits == keyBits)     // Values are equal
                    return mid;             // Key found
                else if (midBits < keyBits) // (-0.0, 0.0) or (!NaN, NaN)
                    low = mid + 1;
                else                        // (0.0, -0.0) or (NaN, !NaN)
                    high = mid - 1;
            }
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array for the specified object using the binary
     * search algorithm. The array must be sorted into ascending order
     * according to the
     * {@linkplain Comparable natural ordering}
     * of its elements (as by the
     * {@link java.util.Arrays#sort(Object[])} method) prior to making this call.
     * If it is not sorted, the results are undefined.
     * (If the array contains elements that are not mutually comparable (for
     * example, strings and integers), it <i>cannot</i> be sorted according
     * to the natural ordering of its elements, hence results are undefined.)
     * If the array contains multiple
     * elements equal to the specified object, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws ClassCastException if the search key is not comparable to the
     *         elements of the array.
     */
    public static int binarySearch(Object[] a, Object key) {
        return binarySearch0(a, 0, a.length, key);
    }
    
    /**
     * Searches a range of
     * the specified array for the specified object using the binary
     * search algorithm.
     * The range must be sorted into ascending order
     * according to the
     * {@linkplain Comparable natural ordering}
     * of its elements (as by the
     * {@link #sort(Object[], int, int)} method) prior to making this
     * call.  If it is not sorted, the results are undefined.
     * (If the range contains elements that are not mutually comparable (for
     * example, strings and integers), it <i>cannot</i> be sorted according
     * to the natural ordering of its elements, hence results are undefined.)
     * If the range contains multiple
     * elements equal to the specified object, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws ClassCastException if the search key is not comparable to the
     *         elements of the array within the specified range.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static int binarySearch(Object[] a, int fromIndex, int toIndex,
                                   Object key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    // Like public version, but without range checks.
    private static int binarySearch0(Object[] a, int fromIndex, int toIndex,
                                     Object key) {
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            @SuppressWarnings("rawtypes")
            Comparable midVal = (Comparable)a[mid];
            @SuppressWarnings("unchecked")
            int cmp = midVal.compareTo(key);
            
            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    /**
     * Searches the specified array for the specified object using the binary
     * search algorithm.  The array must be sorted into ascending order
     * according to the specified comparator (as by the
     * {@link #sort(Object[], Comparator) sort(T[], Comparator)}
     * method) prior to making this call.  If it is
     * not sorted, the results are undefined.
     * If the array contains multiple
     * elements equal to the specified object, there is no guarantee which one
     * will be found.
     *
     * @param <T> the class of the objects in the array
     * @param a the array to be searched
     * @param key the value to be searched for
     * @param c the comparator by which the array is ordered.  A
     *        <tt>null</tt> value indicates that the elements'
     *        {@linkplain Comparable natural ordering} should be used.
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws ClassCastException if the array contains elements that are not
     *         <i>mutually comparable</i> using the specified comparator,
     *         or the search key is not comparable to the
     *         elements of the array using this comparator.
     */
    public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) {
        return binarySearch0(a, 0, a.length, key, c);
    }
    
    /**
     * Searches a range of
     * the specified array for the specified object using the binary
     * search algorithm.
     * The range must be sorted into ascending order
     * according to the specified comparator (as by the
     * {@link #sort(Object[], int, int, Comparator)
     * sort(T[], int, int, Comparator)}
     * method) prior to making this call.
     * If it is not sorted, the results are undefined.
     * If the range contains multiple elements equal to the specified object,
     * there is no guarantee which one will be found.
     *
     * @param <T> the class of the objects in the array
     * @param a the array to be searched
     * @param fromIndex the index of the first element (inclusive) to be
     *          searched
     * @param toIndex the index of the last element (exclusive) to be searched
     * @param key the value to be searched for
     * @param c the comparator by which the array is ordered.  A
     *        <tt>null</tt> value indicates that the elements'
     *        {@linkplain Comparable natural ordering} should be used.
     * @return index of the search key, if it is contained in the array
     *         within the specified range;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element in the range greater than the key,
     *         or <tt>toIndex</tt> if all
     *         elements in the range are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     * @throws ClassCastException if the range contains elements that are not
     *         <i>mutually comparable</i> using the specified comparator,
     *         or the search key is not comparable to the
     *         elements in the range using this comparator.
     * @throws IllegalArgumentException
     *         if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *         if {@code fromIndex < 0 or toIndex > a.length}
     * @since 1.6
     */
    public static <T> int binarySearch(T[] a, int fromIndex, int toIndex,
                                       T key, Comparator<? super T> c) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key, c);
    }
    
    // Like public version, but without range checks.
    private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex,
                                         T key, Comparator<? super T> c) {
        if (c == null) {
            return binarySearch0(a, fromIndex, toIndex, key);
        }
        int low = fromIndex;
        int high = toIndex - 1;
        
        while (low <= high) {
            int mid = (low + high) >>> 1;
            T midVal = a[mid];
            int cmp = c.compare(midVal, key);
            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
    //----------------------------------------------------------------
    //---------------------------------------------------------------- sort
    //----------------------------------------------------------------
    /**
     * Old merge sort implementation can be selected (for
     * compatibility with broken comparators) using a system property.
     * Cannot be a static boolean in the enclosing class due to
     * circular dependencies. To be removed in a future release.
     */
    static final class LegacyMergeSort {
        private static final boolean userRequested = false;
                // AccessController.doPrivileged(
                //         new sun.security.action.GetBooleanAction(
                //                 "java.util.Arrays.useLegacyMergeSort")).booleanValue();
    }
    
    /**
     * Swaps x[a] with x[b].
     */
    private static void swap(Object[] x, int a, int b) {
        Object t = x[a];
        x[a] = x[b];
        x[b] = t;
    }
    
    /**
     * Sorts the specified array of objects according to the order induced by
     * the specified comparator.  All elements in the array must be
     * <i>mutually comparable</i> by the specified comparator (that is,
     * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
     * for any elements {@code e1} and {@code e2} in the array).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param <T> the class of the objects to be sorted
     * @param a the array to be sorted
     * @param c the comparator to determine the order of the array.  A
     *        {@code null} value indicates that the elements'
     *        {@linkplain Comparable natural ordering} should be used.
     * @throws ClassCastException if the array contains elements that are
     *         not <i>mutually comparable</i> using the specified comparator
     * @throws IllegalArgumentException (optional) if the comparator is
     *         found to violate the {@link Comparator} contract
     */
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
    
    /** To be removed in a future release. */
    private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
        T[] aux = a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);
    }
    
    /**
     * Sorts the specified range of the specified array of objects according
     * to the order induced by the specified comparator.  The range to be
     * sorted extends from index {@code fromIndex}, inclusive, to index
     * {@code toIndex}, exclusive.  (If {@code fromIndex==toIndex}, the
     * range to be sorted is empty.)  All elements in the range must be
     * <i>mutually comparable</i> by the specified comparator (that is,
     * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
     * for any elements {@code e1} and {@code e2} in the range).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param <T> the class of the objects to be sorted
     * @param a the array to be sorted
     * @param fromIndex the index of the first element (inclusive) to be
     *        sorted
     * @param toIndex the index of the last element (exclusive) to be sorted
     * @param c the comparator to determine the order of the array.  A
     *        {@code null} value indicates that the elements'
     *        {@linkplain Comparable natural ordering} should be used.
     * @throws ClassCastException if the array contains elements that are not
     *         <i>mutually comparable</i> using the specified comparator.
     * @throws IllegalArgumentException if {@code fromIndex > toIndex} or
     *         (optional) if the comparator is found to violate the
     *         {@link Comparator} contract
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *         {@code toIndex > a.length}
     */
    public static <T> void sort(T[] a, int fromIndex, int toIndex,
                                Comparator<? super T> c) {
        if (c == null) {
            sort(a, fromIndex, toIndex);
        } else {
            rangeCheck(a.length, fromIndex, toIndex);
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, fromIndex, toIndex, c);
            else
                TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
        }
    }
    
    /** To be removed in a future release. */
    private static <T> void legacyMergeSort(T[] a, int fromIndex, int toIndex,
                                            Comparator<? super T> c) {
        T[] aux = copyOfRange(a, fromIndex, toIndex);
        if (c==null)
            mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
        else
            mergeSort(aux, a, fromIndex, toIndex, -fromIndex, c);
    }
    
    /**
     * Src is the source array that starts at index 0
     * Dest is the (possibly larger) array destination with a possible offset
     * low is the index in dest to start sorting
     * high is the end index in dest to end sorting
     * off is the offset into src corresponding to low in dest
     * To be removed in a future release.
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low, int high, int off,
                                  Comparator c) {
        int length = high - low;
        
        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
        
        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off, c);
        mergeSort(dest, src, mid, high, -off, c);
        
        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (c.compare(src[mid-1], src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }
        
        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }
    
    
    /**
     * Sorts the specified array of objects into ascending order, according
     * to the {@linkplain Comparable natural ordering} of its elements.
     * All elements in the array must implement the {@link Comparable}
     * interface.  Furthermore, all elements in the array must be
     * <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
     * not throw a {@code ClassCastException} for any elements {@code e1}
     * and {@code e2} in the array).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param a the array to be sorted
     * @throws ClassCastException if the array contains elements that are not
     *         <i>mutually comparable</i> (for example, strings and integers)
     * @throws IllegalArgumentException (optional) if the natural
     *         ordering of the array elements is found to violate the
     *         {@link Comparable} contract
     */
    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }
    
    /** To be removed in a future release. */
    private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }
    
    /**
     * Sorts the specified range of the specified array of objects into
     * ascending order, according to the
     * {@linkplain Comparable natural ordering} of its
     * elements.  The range to be sorted extends from index
     * {@code fromIndex}, inclusive, to index {@code toIndex}, exclusive.
     * (If {@code fromIndex==toIndex}, the range to be sorted is empty.)  All
     * elements in this range must implement the {@link Comparable}
     * interface.  Furthermore, all elements in this range must be <i>mutually
     * comparable</i> (that is, {@code e1.compareTo(e2)} must not throw a
     * {@code ClassCastException} for any elements {@code e1} and
     * {@code e2} in the array).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element (inclusive) to be
     *        sorted
     * @param toIndex the index of the last element (exclusive) to be sorted
     * @throws IllegalArgumentException if {@code fromIndex > toIndex} or
     *         (optional) if the natural ordering of the array elements is
     *         found to violate the {@link Comparable} contract
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *         {@code toIndex > a.length}
     * @throws ClassCastException if the array contains elements that are
     *         not <i>mutually comparable</i> (for example, strings and
     *         integers).
     */
    public static void sort(Object[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, fromIndex, toIndex);
        else
            ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);
    }
    
    /** To be removed in a future release. */
    private static void legacyMergeSort(Object[] a,
                                        int fromIndex, int toIndex) {
        Object[] aux = copyOfRange(a, fromIndex, toIndex);
        mergeSort(aux, a, fromIndex, toIndex, -fromIndex);
    }
    
    /**
     * Tuning parameter: list size at or below which insertion sort will be
     * used in preference to mergesort.
     * To be removed in a future release.
     */
    private static final int INSERTIONSORT_THRESHOLD = 7;
    
    /**
     * Src is the source array that starts at index 0
     * Dest is the (possibly larger) array destination with a possible offset
     * low is the index in dest to start sorting
     * high is the end index in dest to end sorting
     * off is the offset to generate corresponding low, high in src
     * To be removed in a future release.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;
        
        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
                        ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
        
        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);
        
        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }
        
        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(int[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(long[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(long[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(short[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(short[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(char[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(char[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(byte[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(byte[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>The {@code <} relation does not provide a total order on all float
     * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
     * value compares neither less than, greater than, nor equal to any value,
     * even itself. This method uses the total order imposed by the method
     * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
     * {@code 0.0f} and {@code Float.NaN} is considered greater than any
     * other value and all {@code Float.NaN} values are considered equal.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(float[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>The {@code <} relation does not provide a total order on all float
     * values: {@code -0.0f == 0.0f} is {@code true} and a {@code Float.NaN}
     * value compares neither less than, greater than, nor equal to any value,
     * even itself. This method uses the total order imposed by the method
     * {@link Float#compareTo}: {@code -0.0f} is treated as less than value
     * {@code 0.0f} and {@code Float.NaN} is considered greater than any
     * other value and all {@code Float.NaN} values are considered equal.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(float[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified array into ascending numerical order.
     *
     * <p>The {@code <} relation does not provide a total order on all double
     * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
     * value compares neither less than, greater than, nor equal to any value,
     * even itself. This method uses the total order imposed by the method
     * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
     * {@code 0.0d} and {@code Double.NaN} is considered greater than any
     * other value and all {@code Double.NaN} values are considered equal.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     */
    public static void sort(double[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    
    /**
     * Sorts the specified range of the array into ascending order. The range
     * to be sorted extends from the index {@code fromIndex}, inclusive, to
     * the index {@code toIndex}, exclusive. If {@code fromIndex == toIndex},
     * the range to be sorted is empty.
     *
     * <p>The {@code <} relation does not provide a total order on all double
     * values: {@code -0.0d == 0.0d} is {@code true} and a {@code Double.NaN}
     * value compares neither less than, greater than, nor equal to any value,
     * even itself. This method uses the total order imposed by the method
     * {@link Double#compareTo}: {@code -0.0d} is treated as less than value
     * {@code 0.0d} and {@code Double.NaN} is considered greater than any
     * other value and all {@code Double.NaN} values are considered equal.
     *
     * <p>Implementation note: The sorting algorithm is a Dual-Pivot Quicksort
     * by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. This algorithm
     * offers O(n log(n)) performance on many data sets that cause other
     * quicksorts to degrade to quadratic performance, and is typically
     * faster than traditional (one-pivot) Quicksort implementations.
     *
     * @param a the array to be sorted
     * @param fromIndex the index of the first element, inclusive, to be sorted
     * @param toIndex the index of the last element, exclusive, to be sorted
     *
     * @throws IllegalArgumentException if {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException
     *     if {@code fromIndex < 0} or {@code toIndex > a.length}
     */
    public static void sort(double[] a, int fromIndex, int toIndex) {
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }
    
}
